package com.erlang4j.internal.messages;

import static com.erlang4j.api.Erlang4jMessageLanguage.atom;
import static com.erlang4j.api.Erlang4jMessageLanguage.binding;
import static com.erlang4j.api.Erlang4jMessageLanguage.integer;
import static com.erlang4j.api.Erlang4jMessageLanguage.list;
import static com.erlang4j.api.Erlang4jMessageLanguage.string;
import static com.erlang4j.api.Erlang4jMessageLanguage.tuple;
import junit.framework.TestCase;

import com.ericsson.otp.erlang.OtpErlangList;
import com.ericsson.otp.erlang.OtpErlangObject;
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.erlang4j.api.IBinding;
import com.erlang4j.api.exceptions.Erland4jDataTypeCastException;
import com.erlang4j.api.exceptions.Erlang4JCannotGetKeyFromInvalidBinding;
import com.erlang4j.api.exceptions.Erlang4jKeyNotFoundException;
import com.erlang4j.tests.Erlang4JTestHelper;

public class BindingTest extends TestCase {

	public void testGetRaw() {
		checkGetRaw("X", string("xvalue"),//
				"X", string("xvalue"), "Y", string("yvalue"));

		checkGetRaw("Y", string("yvalue"), "X", string("xvalue"), "Y", string("yvalue"));

		checkGetRaw("Y", atom("yvalue"), "X", string("xvalue"), "Y", atom("yvalue"));
	}

	public void testGetFromInvalid() {
		Erlang4JTestHelper.assertThrows(Erlang4JCannotGetKeyFromInvalidBinding.class, "Cannot get key name [Any] for object of type class com.ericsson.otp.erlang.OtpErlangAtom", new Runnable() {
			public void run() {
				Binding.invalid.getAtom("Any");
			}
		});
	}

	public void testGetString() {
		final IBinding binding = binding("X", string("xvalue"), "Y", string("yvalue"), "a", atom("atom"));
		assertEquals("xvalue", binding.getString("X"));
		assertEquals("yvalue", binding.getString("Y"));

		Erlang4JTestHelper.assertThrows(Erlang4jKeyNotFoundException.class, "Could not find key [Z] in [[X, Y, a]]", new Runnable() {
			public void run() {
				binding.getString("Z"); // Z isnt in the binding
			}
		});
		Erlang4JTestHelper.assertThrows(Erland4jDataTypeCastException.class, "Class cast exception. Expected class class com.ericsson.otp.erlang.OtpErlangString Actual class class com.ericsson.otp.erlang.OtpErlangAtom Object atom", new Runnable() {
			public void run() {
				binding.getString("a"); // a is in but isn't a string
			}
		});
	}

	public void testGetInt() {
		final IBinding binding = binding("X", integer(1), "Y", integer(2), "a", string("1"));
		assertEquals(1, binding.getInt("X"));
		assertEquals(2, binding.getInt("Y"));

		Erlang4JTestHelper.assertThrows(Erlang4jKeyNotFoundException.class, "Could not find key [Z] in [[X, Y, a]]", new Runnable() {
			public void run() {
				binding.getInt("Z"); // Z isn't in the binding
			}
		});
		Erlang4JTestHelper.assertThrows(Erland4jDataTypeCastException.class, "Class cast exception. Expected class class com.ericsson.otp.erlang.OtpErlangInt Actual class class com.ericsson.otp.erlang.OtpErlangString Object \"1\"", new Runnable() {
			public void run() {
				binding.getInt("a"); // a is in but isn't a int
			}
		});
		// Its hard to test the out of range exception because the OtpErlangInteger constructor takes an int
		// Erlang4JTestHelper.assertThrows(Erlang4JOutOfRangeException.class, "", new Runnable() {
		// public void run() {
		// binding.getInt("bigIntegerKey");
		// }
		// });
	}

	public void testGetLong() {
		final IBinding binding = binding("X", integer(1), "Y", integer(2), "a", string("1"));
		assertEquals(1, binding.getLong("X"));
		assertEquals(2, binding.getLong("Y"));

		Erlang4JTestHelper.assertThrows(Erlang4jKeyNotFoundException.class, "Could not find key [Z] in [[X, Y, a]]", new Runnable() {
			public void run() {
				binding.getLong("Z"); // Z isn't in the binding
			}
		});
		Erlang4JTestHelper.assertThrows(Erland4jDataTypeCastException.class, "Class cast exception. Expected class class com.ericsson.otp.erlang.OtpErlangInt Actual class class com.ericsson.otp.erlang.OtpErlangString Object \"1\"", new Runnable() {
			public void run() {
				binding.getLong("a"); // a is in but isn't a long
			}
		});
	}

	public void testGetTuple() {
		OtpErlangTuple tuple = tuple(integer(1), integer(2), string("1"));
		final IBinding binding = binding("X", tuple, "a", atom("a"));
		assertEquals(tuple, binding.getTuple("X"));

		Erlang4JTestHelper.assertThrows(Erlang4jKeyNotFoundException.class, "Could not find key [Z] in [[X, a]]", new Runnable() {
			public void run() {
				binding.getTuple("Z"); // Z isn't in the binding
			}
		});
		Erlang4JTestHelper.assertThrows(Erland4jDataTypeCastException.class, "Class cast exception. Expected class class com.ericsson.otp.erlang.OtpErlangTuple Actual class class com.ericsson.otp.erlang.OtpErlangAtom Object a", new Runnable() {
			public void run() {
				binding.getTuple("a"); // a is in but isn't a tuple
			}
		});
	}

	public void testGetList() {
		OtpErlangList list = list(integer(1), integer(2), string("1"));
		final IBinding binding = binding("X", list, "a", atom("a"));
		assertEquals(list, binding.getList("X"));

		Erlang4JTestHelper.assertThrows(Erlang4jKeyNotFoundException.class, "Could not find key [Z] in [[X, a]]", new Runnable() {
			public void run() {
				binding.getList("Z"); // Z isn't in the binding
			}
		});
		Erlang4JTestHelper.assertThrows(Erland4jDataTypeCastException.class, "Class cast exception. Expected class class com.ericsson.otp.erlang.OtpErlangList Actual class class com.ericsson.otp.erlang.OtpErlangAtom Object a", new Runnable() {
			public void run() {
				binding.getList("a"); // a is in but isn't a list
			}
		});
	}

	private void checkGetRaw(String key, OtpErlangObject expected, Object... bindingDefn) {
		IBinding binding = binding(bindingDefn);
		OtpErlangObject actual = binding.getRaw(key);
		assertEquals(expected, actual);
	}
}
